//
//  main.cpp
//  
//
//  Created by Kun Zhao on 12/06/06.
//  Copyright (c) 2012 Kun Zhao. All rights reserved.
//

#include <kvs/glut/Application>
#include <kvs/glut/Screen>
#include <kvs/StructuredVolumeObject>
#include <kvs/StructuredVolumeImporter>
#include <kvs/UnstructuredVolumeObject>
#include <kvs/LineObject>
#include <kvs/PointObject>
#include <kvs/CellByCellMetropolisSampling>
#include <kvs/glew/ParticleVolumeRenderer>
#include <kvs/glew/StochasticPointRenderer>
#include <kvs/glew/StochasticTetrahedraRenderer>

#include <kvs/KVSMLObjectUnstructuredVolume>
#include <kvs/UnstructuredVolumeExporter>

#include <kvs/TransferFunction>
#include <kvs/ExtractEdges>
#include <kvs/ExtractVertices>
#include <kvs/HydrogenVolumeData>
#include <kvs/TornadoVolumeData>
#include <kvs/CommandLine>
#include <kvs/Timer>
#include "BlockLoader.h"
#include "Rawiv.h"
#include "StructuredVolumeImporter.h"

class Argument : public kvs::CommandLine
{
public:
    
    std::string filename;
    size_t sp;
    float samplingstep;
    kvs::TransferFunction tfunc;
    size_t rl;
    
    size_t nx;
    size_t ny;
    size_t nz;
    
    
    Argument( int argc, char** argv ) : CommandLine ( argc, argv )
    {
        add_help_option();
        add_option( "f", "filename", 1, true );
        add_option( "sp", "subpixel level", 1 , false );
        add_option( "ss", "sampling step", 1, false );
        add_option( "t", "transfer function", 1, false );
        add_option( "PBVR", "with renderer of PBVR", 0, false );
        add_option( "SPT", "with renderer of SPT", 0, false );
        add_option( "Edge", "extract the edge of the the volume", 0, false );
        add_option( "write", "write the block divided volume to this folder", 0, false );
    }
    
    void exec()
    {
        sp = 3;
        samplingstep = 0.5;
        tfunc.create( 256 );
        
        nx = 0;
        ny = 0;
        nz = 0;
        
        if( !this->parse() ) exit( EXIT_FAILURE );
        if( this->hasOption( "f" ) ) filename = this->optionValue<std::string>( "f" );
        if( this->hasOption( "sp" ) ) sp = this->optionValue<size_t>( "sp" );
        if( this->hasOption( "ss" ) ) samplingstep = this->optionValue<float>( "ss" );
        if( this->hasOption( "t" ) ) tfunc = kvs::TransferFunction( this->optionValue<std::string>( "t" ) );
        
        rl = sp * sp;
        
    }
};

kvs::UnstructuredVolumeObject* BlockImporter( std::string filename )
{
    FILE *rawfile = fopen( filename.c_str(), "rb" );
	fseek( rawfile, 0, SEEK_END );
	size_t end = ftell( rawfile );
	size_t length = end / sizeof(float);
    std::cout << length << std::endl;
	fseek( rawfile, 0 , SEEK_SET );    
    float* buf = new float[length];
    fread( buf, sizeof( float ), length, rawfile );
    fclose(rawfile);
    
    unsigned int block_size = (unsigned int)buf[0];
    unsigned int nx = (unsigned int)buf[1]; 
    unsigned int ny = (unsigned int)buf[2]; 
    unsigned int nz = (unsigned int)buf[3];
    length -= 4;
    float* values = new float[length];
    for( size_t i = 0; i < length; i++ ) values[i] = buf[i + 4];
    delete buf;
    std::cout << "finish load file" << std::endl;
    
    kvs::Timer time1;
    time1.start();
    kvs::UnstructuredVolumeObject* object = new kvs::BlockLoader( values, block_size, nx, ny, nz );
    time1.stop();
    std::cout << "decompression time: " << time1.msec() << "msec" <<  std::endl;
    return( object );
}

int main( int argc, char** argv )
{
    kvs::glut::Application app( argc, argv );
    Argument param( argc, argv );
    param.exec();
    
    kvs::Timer time;
    time.start();
    kvs::UnstructuredVolumeObject* volume = BlockImporter( param.filename );
    time.stop();
    std::cout << "Construction time: " << time.msec() << "msec" << std::endl;
    
    kvs::glut::Screen screen( &app );
        
    if( param.hasOption( "SPT" ) )
    {   
        kvs::glew::StochasticTetrahedraRenderer* renderer_SPT = new kvs::glew::StochasticTetrahedraRenderer();
        renderer_SPT->setRepetitionLevel( param.rl );
        renderer_SPT->setTransferFunction( param.tfunc );
        renderer_SPT->enableLODControl();
        
        screen.registerObject( volume, renderer_SPT );
        screen.setTitle( "SPT Renderer");
        
        std::cout << "SPT process has been done" << std::endl;
    }
    if( param.hasOption( "PBVR" ) )
    {
        kvs::PointObject* object = new kvs::CellByCellMetropolisSampling(
                                                                         volume,
                                                                         param.sp,
                                                                         param.samplingstep,
                                                                         param.tfunc,
                                                                         0.0f
                                                                         );
        kvs::glew::ParticleVolumeRenderer* renderer_PBVR = new kvs::glew::ParticleVolumeRenderer();
        renderer_PBVR->setRepetitionLevel( param.rl );
        renderer_PBVR->enableShading();
        renderer_PBVR->setShader( kvs::Shader::Phong( 0.5, 0.5, 0.8, 15.0 ) );
        //renderer_PBVR->disableShading();
        renderer_PBVR->disableZooming();
        
        screen.registerObject( object, renderer_PBVR );
        screen.setTitle( "PBVR Renderer");
        
        std::cout << "PBVR process has been done" << std::endl;
	    std::cout << *object << std::endl;
        delete volume;
    }
    if( param.hasOption( "Edge" ) )
    {
        kvs::LineObject* line = new kvs::ExtractEdges( volume );
        screen.registerObject( line );
        screen.setTitle( "ExtractEdges");
        
        std::cout << "ExtractEdges process has been done" << std::endl;
        delete volume;
    }
    
    screen.background()->setColor( kvs::RGBColor( 255, 255, 255 ));
    screen.setGeometry( 0, 0, 1024, 768 );
    screen.show();
    
    return( app.run() );
}
